package com.xigege.netty.simpleExample;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.util.CharsetUtil;

import java.util.concurrent.TimeUnit;

/**
 * 1. 自定义一个pipeline的handler，需要继承netty规定好的某个HandlerAdapter
 *      这个自定义的handler才能为pipeline所使用
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    //读取数据事件（这里我们可以读取客户端发送的消息）
    /**
     * 1. ChannelHandlerContext：上下文对象，含有管道pipeline和通道channel、连接的地址等
     *      pipeline：可以关联很多的handler，流水线式的使用handler进行处理，可以简单理解为业务逻辑处理管道
     *      channel：更加注重的是数据的读、写，是一个数据通道
     * 2. Object msg 就是客户发送的数据 默认是Object
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
       /* System.out.println("服务器读取线程" + Thread.currentThread().getName());
        System.out.println("server ctx = " + ctx);

        System.out.println("看看channel和pipeline的关系");
        //拿到channel
        Channel channel = ctx.channel();
        //拿到pipeline，pipeline底层数据结构为一个双向链表，出栈、入栈
        ChannelPipeline pipeline = ctx.pipeline();

        //nio模型拿到数据后先放到buffer里面
        //将msg 转成一个ByteBuf
        //ByteBuf是Netty提供的，不是Nio的byteBuffer
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("客户端发送的消息是：" + buf.toString(CharsetUtil.UTF_8));
        System.out.println("客户端的地址是：" + channel.remoteAddress());*/

       //比如我们这里有一个非常耗时的业务 -> 希望能够异步执行 -> 提交到该channel对应的 NIOEventLoop对应的taskQueue中
        try { TimeUnit.SECONDS.sleep( 10 ); } catch (InterruptedException e) { e.printStackTrace(); }
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端 ", CharsetUtil.UTF_8));
        System.out.println("go on ....");

        //解决方法1 用户程序自定义的普通方法
        ctx.channel().eventLoop().execute(()->{
            try {
                Thread.sleep(10 * 1000);
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端 ", CharsetUtil.UTF_8));
            } catch (Exception e) {
                System.out.println("发生异常" + e.getMessage());
                e.printStackTrace();
            }
        });

        ctx.channel().eventLoop().execute(()->{
            try {
                Thread.sleep(20 * 1000);
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端 ", CharsetUtil.UTF_8));
            } catch (Exception e) {
                System.out.println("发生异常" + e.getMessage());
                e.printStackTrace();
            }
        });

        //用户自定义定时任务 -> 该任务是提交到 scheduleTaskQueue中
        //schedule方法中传入1. runnable接口。 2. 延时多久执行。 3. 时间单位
        ctx.channel().eventLoop().schedule(()->{
            try {
                Thread.sleep(10 * 1000);
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端 ", CharsetUtil.UTF_8));
            } catch (Exception e) {
                System.out.println("发生异常" + e.getMessage());
                e.printStackTrace();
            }
        },5 , TimeUnit.SECONDS);
    }

    //读取数据完毕
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //writeAndFlush 是 write + flush
        //把数据写到一个缓冲区同时刷新该缓冲（把数据打到管道里面去，这样数据才能得到）
        //一般讲，我们要对这个发送的数据进行编码
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello，客户端~",CharsetUtil.UTF_8));
    }

    //处理异常，一般是需要关闭通道
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}
